import logging
from pathlib import Path
from functools import cached_property
from pydantic import BaseModel, Field, computed_field, model_validator
import cnvpytor
from .image import Image
from .mergecn import MergeCN
from .vcf import VCF
from .gender import Gender
from .app import App

logging.basicConfig(level=logging.INFO, format="%(asctime)s - %(name)s - %(levelname)s - %(message)s")
logger = logging.getLogger("ClinCNVseq")


class CNVseq(BaseModel):
    bam_file: Path
    outdir: Path
    bin_size: int
    thread: int
    max_female: float
    min_male: float
    gap_file: Path
    genepred_file: Path | None
    gene_id_file: Path | None
    cytoband_file: Path | None
    bed_files: list[Path] | None
    overlap: float
    dbdir: str | None
    custom_gender: str | None = Field(alias="gender", exclude=True)

    @model_validator(mode="after")
    def validate_parameters(self):
        if self.custom_gender:
            self.gender_file.write_text(self.custom_gender)
        return self

    @computed_field
    @cached_property
    def sample(self) -> str:
        return self.bam_file.name.split(".")[0]

    @cached_property
    def app(self) -> cnvpytor.Root:
        logger.info(f"Loading pytor: {self.pytor_file}")
        return cnvpytor.Root(str(self.pytor_file), create=False, max_cores=self.thread)

    @computed_field
    @cached_property
    def gender(self) -> str:
        return self.gender_file.read_text().strip()

    @computed_field
    @cached_property
    def pytor_file(self) -> Path:
        return self.outdir / f"{self.sample}.pytor"

    @computed_field
    @cached_property
    def gender_file(self) -> Path:
        return self.outdir / f"{self.sample}.gender.txt"

    @computed_field
    @cached_property
    def call_vcf_file(self) -> Path:
        return self.outdir / f"{self.sample}.call.vcf"

    @computed_field
    @cached_property
    def vcf_file(self) -> Path:
        return self.outdir / f"{self.sample}.cnv.vcf"

    @computed_field
    @cached_property
    def mask_cn_file(self) -> Path:
        return self.outdir / f"{self.sample}.mask.cn.tsv"

    @computed_field
    @cached_property
    def raw_cn_file(self) -> Path:
        return self.outdir / f"{self.sample}.raw.cn.tsv"

    @computed_field
    @cached_property
    def multi_cn_file(self) -> Path:
        return self.outdir / f"{self.sample}.cn.tsv"

    @computed_field
    @cached_property
    def control_cn_files(self) -> list[Path]:
        if self.dbdir:
            dbdir = self.dbdir / self.gender
            return list(filter(lambda x: x.name.split(".")[0] != self.sample, dbdir.glob("*.cn.tsv")))[0:10]
        return []

    @computed_field
    @cached_property
    def image_file(self) -> Path:
        return self.outdir / f"{self.sample}_cnv_plot.zip"

    def run(self):
        self.outdir.mkdir(exist_ok=True, parents=True)
        Gender(bam_file=self.bam_file, gender_file=self.gender_file, max_female=self.max_female, min_male=self.min_male).run()
        kwargs = self.model_dump()
        App(**kwargs).run()
        VCF(app=self.app, **kwargs).run()
        MergeCN(app=self.app, **kwargs).run()
        Image(**kwargs).run()
